home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene 96
/
Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso
/
misc
/
coding
/
vgacodng
/
part03.txt
< prev
next >
Wrap
Text File
|
1996-08-07
|
14KB
|
333 lines
VGA-Kurs - Part #3
"T.C.P.'s Beginner's Guide To VGA Coding"(TM) ist wieder da mit Teil III!
Zu Beginn als Appetithäppchen eine erneut schnellere PutPixel-Routine. Die ist
jetzt meiner Meinung nach nicht schneller zu machen (höchstens in 386er Code).
Wer anderer Meinung ist, soll seine Lösung hier präsentieren.
Wichtig bei dieser Prozedur ist, das stets ein GLOBALE Konstante
'VGA : word = $A000;' deklariert wird.
const VGA : word = $A000;
procedure PutPixel(x,y:word;c:byte);assembler;
asm
mov es,VGA
mov di,x
mov dx,y
mov bx,dx
shl dx,8
shl bx,6
add dx,bx
add di,dx
mov al,c
stosb
end;
Außerdem noch unentbehrlich: Eine GetPixel-Funktion. Diese ermittelt den
Farbwert des Pixels, der an den angegebenen Koordinaten steht.
function GetPixel(x,y:word) : byte;assembler;
asm
mov es,vga
mov di,x
mov dx,y
mov bx,dx
shl dx,8
shl bx,6
add dx,bx
add di,dx
mov al,es:[di]
mov [bp-1],al
end;
Diese Funktion verfährt bei der Berechnung des Offsets des Pixels haargenau
so wie die Schwester-Prozedur. Nur am Schluß gibt es eine kleine Änderung.
Statt den Pixel zu schreiben wird der Farbwert, der bekanntlich an der
Adresse A000h:Y*320+X steht, in AL ausgelesen und dann als Funktionsergebnis
zurückgeliefert.
Nun wäre es vielleicht an der Zeit, daß sich der geneigte Leser eine Unit
mit den besprochenen Routinen zusammenstellt, um die Beispiele einfacher
kompilieren zu können und eigene Programme schneller zu erstellen.
Aber nun zur Sache: In dieser Ausgabe werden wir uns einem häufig besprochenen
und geheimnisumwitterten Thema widmen: Dem Scrolly.
Was ein Scrolly ist, weiß wahrscheinlich jeder. Eine Zeichenkette wird zum
angenehmeren Lesen von rechts nach links ge(sc)rollt. Dabei wird zuerst ein
Teil der Laufschrift am rechten Rand des Screens gezeigt, dann ein Stück nach
links bewegt und schließlich der nächste Teil angefügt. Einfaches Prinzip,
große Wirkung. Um wirklich zu beeindrucken, sollte der Scrolly aber nicht nur
einfach scrollen sondern z.B. noch ein Plasma im Hintergrund bewegt werden
oder Ähnliches.
Beginnen wir im Praxisteil mit einem einfachen Textmode-Scrolly. Der Text wird
in der Konstanten Text abgelegt. Nun wird innerhalb der Schleife zuerst der
Buchstabe des Textes auf den Screen geschrieben (Zeile 13), dessen Nummer in
der Variablen Charno steht. Nun wird die komplette Zeile ab dem 2. Zeichen um
1 Zeichen nach links verschoben. Dazu folgendes: Wir haben in der ersten
Ausgabe gelernt wie der Bildschirmspeicher ab Adresse A000h organisiert ist.
Zur Erinnerung: Die Pixelinformationen sind hintereinander in einem 64000 Byte
großen Bereich abgelegt. Die Offset-Adresse läßt sich nach der Formel
Offset = Y-Koord * 320 + X-Koord berechnen. Nun, im Textmodus ist dies so
ähnlich. Der Bildschirmspeicher liegt an der Adresse B800h (B000h bei
Hercules), und ist 4000 Byte groß. Bei Segment B800, Offset 0 liegt also der
ASCII-Code des ersten Zeichens auf dem Bildschirm. Steht nun auf dem Screen
bei den Koords (0,0) ein "A", so findet man bei Adresse $B800:0 den Wert 65,
also den ASCII-Code von "A". An Adresse $B800:1 liegt nun aber nicht, wie zu
vermuten wäre, der ASCII-Code des zweiten Zeichens, Koords (1,0), sondern das
sog. Attributbyte des ersten Zeichens. Dieses beeinhaltet die Farbe und die
Art des Zeichens, z.B. rot und blinkend. Um nun dem "A" die Farbe Rot und das
Attribut "blinkend" zuzuweisen, rechnet man den Wert für die Farbe Rot (4) und
für "blinkend" (128) zusammen, erhält 132, und schreibt diesen Wert an die
Adresse $B800:1. Unter Pascal kann man zum Ändern von Textfarbe und Attribut
auch die Prozedur "Textcolor" der Unit Crt benutzen. Rot und blinkend stellt
man z.B. ein mit "Textcolor(red+blink);".
Die Formel zum Berechnen der Adresse eines Zeichens ist also:
Adresse = (Y-Koord * 80 + X-Koord) * 2.
Zurück zu unserem Scrolly. Mit der Standard-Prozedur "Move" werden nun ab der
Adresse $B800:1922 158 Bytes (79 Zeichen) nach $B800:1920 (also um ein Zeichen
nach links) kopiert. Nun wird nur noch die Nummer des aktuellen Buchstabens
erhöht und überprüft, ob die Zeichenkette bereits am Ende ist, und wenn ja
wird Charno wieder auf 1 gesetzt. Dies geht solange weiter, bis eine Taste
gedrückt wird.
Achso: Generell ist es bei Scrollies natürlich wichtig, auf ein entsprechend
flüssiges Scrolling zu achten. Dies besorgt die Prozedur "WaitRetrace", die
wir schon in der letzten Ausgabe kennengelernt haben. Ihr könnt das
"Waitretrace" ja mal aus der Schleife entfernen und durch ein "Delay(10)"
ersetzen, und zusehen, was für ein Geruckel dabei herauskommt.
Augenbeleidigend!
program Scrolly1;
uses crt;
const Text : string = 'Hallo, dies ist ein Test Sc'+
'rolly, der sich solange wie'+
'derholt, bis ein Taste gedr'+
'ückt wird..................';
var Charno : byte;
procedure WaitRetrace;assembler;
asm
mov dx,3DAh
@l1: in al,dx
and al,08h
jz @l1
@l2: in al,dx
and al,08h
jz @l2
end;
begin
Charno := 1; { Erstes Zeichen des Scrolltextes }
clrscr; { Bildschirm löschen }
gotoxy(80,13); { Zum letzten Zeichen in Zeile 13 }
repeat
WaitRetrace;
write(Text[Charno]); { Zeichen schreiben }
move(mem[$B800:1922],mem[$B800:1920],158);
{ 79 Zeichen um 1 Zeichen nach links schieben }
inc(Charno); { Nächstes Zeichen }
if Charno > length(Text) then Charno := 1;
{ Wenn Zeichenkette am Ende, von vorne beginnen }
gotoxy(80,13); { Wieder zum Ausgangspunkt }
until keypressed;
readkey;
end.
Mal wieder eine Anmerkung: Der GotoXY-Befehl benutzt, wahrscheinlich um den
Umgang mit der Prozedur für Anfänger zu erleichtern, ein Koordinatensystem,
das bei (1,1) beginnt. Wenn ihr also zu (20,20) wollt, müßt ihr GotoXY(21,21)
eingeben. Dies ist oft sehr verwirrend, und man kann leider nur Abhilfe
schaffen, wenn man sich eine eigene GotoXY-Variante schreibt.
So, das waren erstmal die Grundlagen. Aber was wir wollten, war ja ein Scrolly
im VGA-Modus. Doch hier ist das Ganze nicht mehr so einfach. Als erstes
benötigen wir einen Font, denn sonst haben wir ja nichts zum Scrollen. Ich
gehe mal davon aus, daß keiner von euch gewillt ist, sich jetzt einen eigenen
Font zu zeichnen, also müssen wir uns einen aus dem BIOS-Rom holen und so
einrichten, daß wir frei auf ihn zugreifen können.
var FontSeg,FontOfs : word;
procedure GetFont;assembler;
asm
mov ax,1130h
mov bh,3 { Font-Nummer für 8x8-Font }
int 10h
mov FontSeg,es
{ Segment, in dem der Font abgelegt ist }
mov FontOfs,bp
{ Offset des Fonts }
end;
Rufen wir diese Prozedur auf, haben wir an Adresse FontSeg:FontOfs den 8 mal 8
Pixel-BIOS-Font. Ein Buchstabe ist also 8 Byte groß. Im ersten Byte stehen in
den einzelnen Bits die Informationen der ersten Zeile des Buchstabens, im
zweiten Byte die Bits der zweiten Reihe usw. Um nun festzustellen, ob im Font
ein Pixel gesetzt (Bit=1) ist oder nicht (Bit=0), muß man die Bits mit den
entsprechenden Potenzen von 2 und-verknüpfen.
Im folgenden VGA-Modus-Scrolly wird also zuerst die erste Spalte des ersten
Buchstabens angezeigt, auf den Retrace gewartet, dann die komplette Zeile um
ein Pixel nach links verschoben und die nächste Spalte auf den Screen
geschrieben.
Die Nummer des Buchstabens steht wieder in Charno, die Spalte in Charpos.
In Character wird der ASCII-Code des aktuellen Zeichens abgelegt.
program Scrolly2;
uses crt;
const VGA = $A000;
Bits : array[0..7] of byte =
(128,64,32,16,8,4,2,1);
Text : string = 'Ein Scrolly im VGA-Modus 13'+
'h, er scrollt und scrollt u'+
'nd scrollt.................';
var FontSeg,FontOfs : word;
{ Hier die Prozeduren GetFont und WaitRetrace
einsetzen }
procedure Scroll;
var I,J : word;
CharPos,CharNo,Color,Character : byte;
begin
CharNo := 1; { Anfangsposition }
repeat
Character := ord(Text[CharNo]);
{ ASCII-Code holen }
for CharPos := 0 to 7 do begin { 8x8 Pixel je }
for I := 0 to 7 do begin { Zeichen }
if mem[FontSeg:FontOfs+(Character*8)+I] and
Bits[CharPos] <> 0 then Color := 31
else Color := black;
{ Wenn daß entsprechende Bit gesetzt ist, }
{ dann Farbe weiß (31) setzen, andernfalls }
{ schwarz. }
mem[$A000:((100+I)*320)+319] := Color;
{ Pixel setzen }
end;
WaitRetrace;
for J := 0 to 7 do for I := 0 to 318 do
mem[$A000:((100+J)*320)+I] :=
mem[$A000:((100+J)*320)+1+I];
{ Alles um einen Pixel nach links bewegen }
end;
inc(CharNo);
if CharNo > length(Text) then CharNo := 1;
until keypressed;
readkey;
end;
begin
GetFont;
asm mov ax,13h; int 10h end; { VGA-Modus 13h }
Scroll;
asm mov ax,03h; int 10h end; { Textmodus }
end.
Dieser Scrolly würde wahrscheinlich in einem Demo sehr wenig Anklang finden,
da der Font nicht gerade zu den allerhübschesten zählt. Außerdem ist er bloß
einfarbig. Letzteres Problem läßt sich allerdings sehr leicht aus der Welt
schaffen.
Deklariert einfach ein Array of byte mit den gewünschten Farben der Zeilen.
Zum Beispiel:
const Colors : array[0..7] of byte = (25,27,29,31,31,29,27,25);
Nun ersetzt ihr im Listing die Zeile "Bits[CharPos] <> 0 then Color := 31"
durch "Bits[CharPos] <> 0 then Color := Colors[I]". Dadurch wird jeder Zeile
des Scrollies ein der Reihe nach der entsprechende Farbwert aus dem
Colors-Array zugewiesen. In diesem Fall beeinhaltet das Array einen
Grau-Verlauf. Übrigens stellen in der Standard-Palette (s. Teil II) die Farben
Nummer 16 bis 31 einen Grau-Verlauf bereit (16=Schwarz, 31=Weiß), den man bei
Bedarf nutzen sollte.
Wer das Beispiel aufmerksam studiert hat, dem wird es auch nicht schwerfallen,
einen eigenen Font einzubauen.
So viel zu horizontalen Scrollies. Natürlich gibt es auch vertikale Scrollies
(eines der besten Beispiele ist am Ende von 2nd Reality zu bestaunen).
Diese sind im Modus 13h zwar ruckelfrei realisierbar, doch durch das Fehlen
weiterer Bildschirmseiten (wie im Mode-X, s.u.) wird dies ein sinnloses
Unterfangen, denn es kann maximal eine Seite gescrollt werden, dann ist das
Ende der Fahnenstange erreicht.
Hier jedoch trotzdem ein solcher Scrolly im Mode 13h, nur um das Prinzip zu
verdeutlichen:
program VertScrolly;
uses crt;
const VGA : word = $A000;
var i,j : word;
c : char;
{ Hier Prozedur WaitRetrace einfügen }
procedure SetStart(Adresse:word);assembler;
asm
mov dx,3D4h { CRTC-Indexregister }
mov al,0Ch
mov ah,byte ptr Adresse + 1
out dx,ax
mov al,0Dh
mov ah,byte ptr Adresse
out dx,ax
end;
begin
randomize; { "Zufalls"zahlen generieren }
asm mov ax,13h; int 10h end; { VGA-Modus 13h }
for i := 0 to 65535 do mem[vga:i] := random(256);
{ Bildschirm füllen }
i := 0;
j := 80;
repeat
if keypressed then begin
c := readkey;
if c = ' ' then j := -j { Richtung umkehren }
else exit;
end;
delay(10); { Ohne Delay zu schnell }
waitretrace;
SetStart(i); { Neue Startadresse setzen }
inc(i,j); { Zähler erhöhen/vermindern }
until keypressed;
readkey;
asm mov ax,03h; int 10h end; { Textmodus }
end.
Die Prozedur SetStart bildet den Kern dieses Programms. Sie stellt mittels des
CRTC ein, an welcher Adresse der Bilschirm 'beginnt'. Dabei wird kein einziges
Byte kopiert oder bewegt, sondern bloß die Adresse, ab der die VGA die im
Bildschirmspeicher abgelegten Informationen auf den Monitor bringt, verändert.
Ruft man sie also mit
SetStart(32000);
auf, so wird der Bildschirminhalt erst ab dem Offset 32000 dargestellt. In
diesem Modus gibt es aber wie gesagt den Nebeneffekt, daß hier in der zweiten
Hälfte des Bildschirms der übersprungene Teil wieder angefügt wird.
Einen anderen Bereich der Scrollies, das Full-Screen-Scrolling, werden wir
später behandeln, wenn wir zum Thema "Mode-X" kommen. Der Modus 13h hat nämlich
das Manko, daß er nur eine Bildschirmseite unterstützt und so den Speicher der
VGA-Karten (heutzutage mind. 512KB) kaum ausnutzt. Deshalb nützt das Scrolling
des gesamten Bildschirminhalt mittels des CRTC (Cathode Ray Tube Controller)
wenig, da immer nur derselbe Bildschirminhalt gescrollt werden kann. Im Mode-X
(oder Chain-4) dagegen hat man bis zu 4 Virtuelle Bildschirme zur Verfügung.
Aber das besprechen wir noch ausführlich in einem der nächsten Teile.
Also, das war's zum Thema Scrollies, Thema von Part IV wahrscheinlich:
Sprites.
[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
[ ]
[ No part of this document may be reproduced, transmitted, ]
[ transcribed, stored in a retrieval system, or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical, magnetic, optical, chemical, manual or otherwise, ]
[ without the expressed written permission of the author. ]
[ ]
[ The information contained in this text is believed to be correct. ]
[ The text is subject to change without notice and does not represent ]
[ a commitment on the part of the author. ]
[ The author does not make a warranty of any kind with regard to this ]
[ material, including, but not limited to, the implied warranties of ]
[ merchantability and fitness for a particular purpose. The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material. ]